package top.songxs.gmall.search.biz.impl;

import cn.hutool.core.util.PageUtil;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import top.songxs.gmall.search.biz.GoodsBizService;
import top.songxs.gmall.search.dto.SearchParamDTO;
import top.songxs.gmall.search.entity.Goods;
import top.songxs.gmall.search.entity.SearchAttr;
import top.songxs.gmall.search.repository.GoodsRepository;
import top.songxs.gmall.search.vo.SearchOrderMapVo;
import top.songxs.gmall.search.vo.SearchRespAttrVo;
import top.songxs.gmall.search.vo.SearchResponseVo;
import top.songxs.gmall.search.vo.SearchTmVo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class GoodsBizServiceImpl implements GoodsBizService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public void saveGoods(Goods goods) {
        goodsRepository.save(goods);
    }

    @Override
    public void deleteGoods(Long skuId) {
        goodsRepository.deleteById(skuId);
    }


    @Override
    public SearchResponseVo searchGoods(SearchParamDTO searchParamDTO) {


        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (searchParamDTO.getCategory1Id() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category1Id", searchParamDTO.getCategory1Id()));
        }
        if (searchParamDTO.getCategory2Id() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category2Id", searchParamDTO.getCategory2Id()));
        }
        if (searchParamDTO.getCategory3Id() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id", searchParamDTO.getCategory3Id()));
        }
        //根据关键字进行搜索
        if (!StringUtils.isEmpty(searchParamDTO.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", searchParamDTO.getKeyword()));
        }
        //根据品牌进行搜索
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            long tmId = Long.parseLong(trademark.split(":")[0]);
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId", tmId));
        }

        //按照平台属性进行搜索
        String[] props = searchParamDTO.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                //例子
                //属性条件【"23:8G:运行内存"，"24:128G:机身内存"】
                String[] split = prop.split(":");
                BoolQueryBuilder boolPropsQueryBuilder = QueryBuilders.boolQuery();
                boolPropsQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                boolPropsQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                boolQueryBuilder.must(QueryBuilders.nestedQuery("attrs", boolPropsQueryBuilder, ScoreMode.None));
            }
        }
        //设置分页条件
        PageRequest pageAble = PageRequest.of(searchParamDTO.getPageNo() - 1, searchParamDTO.getPageSize());

        NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(boolQueryBuilder).setPageable(pageAble);
        if (!StringUtils.isEmpty(searchParamDTO.getKeyword())) {
            //设置高亮查询条件
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title")
                    .preTags("<font color='red'>")
                    .postTags("</font>");

            HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
            nativeSearchQuery.setHighlightQuery(highlightQuery);
        }


        // 设置排序参数
        String order = searchParamDTO.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            switch (split[0]) {
                case "1":
                    nativeSearchQuery.addSort(Sort.by(split[1].equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "hotScore"));
                    break;
                case "2":
                    nativeSearchQuery.addSort(Sort.by(split[1].equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "price"));
                    break;
            }
        }
        //设置品牌聚合的相关参数；
        TermsAggregationBuilder tmIdAgg = AggregationBuilders.terms("tmIdAgg").field("tmId").size(100);
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName").size(100));
        tmIdAgg.subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl").size(100));
        nativeSearchQuery.addAggregation(tmIdAgg);
        //设置平台属性的相关参数
        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders
                .nested("attrsAgg", "attrs")
                .subAggregation(AggregationBuilders
                        .terms("attrIdAgg").field("attrs.attrId").size(100)
                        .subAggregation(AggregationBuilders
                                .terms("attrNameAgg").field("attrs.attrName").size(100))
                        .subAggregation(AggregationBuilders
                                .terms("attrValueAgg").field("attrs.attrValue").size(100)));
        nativeSearchQuery.addAggregation(nestedAggregationBuilder);

        //发送es查询；
        SearchHits<Goods> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Goods.class);

        // 返回结果//解析返回结果
        return this.parseReponseResult(searchHits, searchParamDTO);

    }

    @Override
    public void updateHotScore(Long goodsId, Integer hotScore) {
        Optional<Goods> goodsOptional = goodsRepository.findById(goodsId);
        Goods goods = goodsOptional.get();
        goods.setHotScore(Long.parseLong(String.valueOf(hotScore.intValue())));
        goodsRepository.save(goods) ;

    }

    //解析响应结果数据
    private SearchResponseVo parseReponseResult(SearchHits<Goods> searchHits, SearchParamDTO searchParamDTO) {
        // 创建SearchResponseVo对象
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        // 封装搜索条件
        searchResponseVo.setSearchParam(searchParamDTO);
        // 封装品牌面包屑
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            searchResponseVo.setTrademarkParam("品牌:" + trademark.split(":")[1]);
        }
        // 设置平台属性面包屑
        ArrayList<SearchAttr> propsParamList = new ArrayList<>();
        String[] props = searchParamDTO.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                String[] split = prop.split(":");
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(Long.parseLong(split[0]));
                searchAttr.setAttrValue(split[1]);
                searchAttr.setAttrName(split[2]);
                propsParamList.add(searchAttr);
            }
            searchResponseVo.setPropsParamList(propsParamList);
        }

        // 设置排序字段
        String orderString = searchParamDTO.getOrder();
        SearchOrderMapVo searchOrderMapVo = new SearchOrderMapVo("1", "desc");
        if (!StringUtils.isEmpty(orderString) && orderString.contains(":") && !"null".equalsIgnoreCase(orderString)) {
            String[] split = orderString.split(":");
            searchOrderMapVo.setSort(split[1]);
            searchOrderMapVo.setType(split[0]);
        }
        searchResponseVo.setOrderMap(searchOrderMapVo);

        // 设置商品列表
        List<SearchHit<Goods>> hits = searchHits.getSearchHits();
        List<Goods> goodsList = new ArrayList<>();
        hits.stream().forEach((SearchHit<Goods> goodsSearchHit) -> {
            Goods goods = goodsSearchHit.getContent();
            Map<String, List<String>> highlightFields = goodsSearchHit.getHighlightFields();
            if (highlightFields != null && highlightFields.size() > 0) {
                List<String> titles = highlightFields.get("title");
                if (titles != null && titles.size() > 0) {
                    goods.setTitle(titles.get(0));
                }
            }
            goodsList.add(goods);
        });
        searchResponseVo.setGoodsList(goodsList);
        // 设置分页参数
        int totalPage = PageUtil.totalPage((int) (searchHits.getTotalHits()), searchParamDTO.getPageSize());
        searchResponseVo.setPageNo(searchParamDTO.getPageNo());
        searchResponseVo.setTotalPages(Long.parseLong(totalPage + ""));

        //:设置搜索面板需要的品牌数据
        Aggregations aggregations = searchHits.getAggregations();
        ParsedLongTerms tmIdAgg = aggregations.get("tmIdAgg");
        List<? extends Terms.Bucket> buckets = tmIdAgg.getBuckets();
        List<SearchTmVo> searchTmVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String tmId = bucket.getKeyAsString();
            Aggregations subAggregation = bucket.getAggregations();
            //获取品牌名称
            ParsedStringTerms tmNameAggs = subAggregation.get("tmNameAgg");
            List<? extends Terms.Bucket> subBuckets = tmNameAggs.getBuckets();
            String tmName = subBuckets.get(0).getKeyAsString();
            //获取品牌LogoUrl
            ParsedStringTerms tmLogoUrlAggs = subAggregation.get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAggs.getBuckets().get(0).getKeyAsString();
            SearchTmVo searchTmVo = new SearchTmVo();
            searchTmVo.setTmId(Long.parseLong(tmId));
            searchTmVo.setTmName(tmName);
            searchTmVo.setTmLogoUrl(tmLogoUrl);
            searchTmVos.add(searchTmVo);
        }
        searchResponseVo.setTrademarkList(searchTmVos);

        //设置urlParams
        String urlParam = this.getUrlParam(searchParamDTO);
        searchResponseVo.setUrlParam(urlParam);
        //设置搜索面板的平台属性的数据
        ParsedNested attrsAgg = aggregations.get("attrsAgg");
        Aggregations attrAggAggregations = attrsAgg.getAggregations();
        ParsedLongTerms attrIdAgg = attrAggAggregations.get("attrIdAgg");
        List<? extends Terms.Bucket> attrsBuckets = attrIdAgg.getBuckets();
        //设置list
        List<SearchRespAttrVo> list = new ArrayList<>();
        for (Terms.Bucket attrsBucket : attrsBuckets) {
            SearchRespAttrVo searchRespAttrVo = new SearchRespAttrVo();
            //设置id
            Long attrIdString = Long.parseLong(attrsBucket.getKeyAsString());
            searchRespAttrVo.setAttrId(attrIdString);
            //设置聚合的name
            Aggregations subAggregation = attrsBucket.getAggregations();
            ParsedStringTerms attrNameAgg = subAggregation.get("attrNameAgg");
            String attrsNameString = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchRespAttrVo.setAttrName(attrsNameString);
            //设置聚合的value
            Aggregations attrValueAggregations = attrsBucket.getAggregations();
            ParsedStringTerms attrValueAgg = attrValueAggregations.get("attrValueAgg");
            List<? extends Terms.Bucket> valueBuckets = attrValueAgg.getBuckets();
            ArrayList<String> strings = new ArrayList<>();
            for (Terms.Bucket valueBucket : valueBuckets) {
                String value = valueBucket.getKeyAsString();
                strings.add(value);
            }
            searchRespAttrVo.setAttrValueList(strings);
            list.add(searchRespAttrVo);

        }
        searchResponseVo.setAttrsList(list);


        return searchResponseVo;

    }

    private String getUrlParam(SearchParamDTO searchParamDTO) {
        StringBuilder sb = new StringBuilder("list.html?");
        // 拼接分类id参数
        //list.html?category1Id=2&trademark=1:小米&&order=2:desc
        if (!StringUtils.isEmpty(searchParamDTO.getCategory1Id())) {
            sb.append("&category1Id=").append(searchParamDTO.getCategory1Id());
        }
        if (!StringUtils.isEmpty(searchParamDTO.getCategory2Id())) {
            sb.append("&category2Id=").append(searchParamDTO.getCategory2Id());
        }
        if (!StringUtils.isEmpty(searchParamDTO.getCategory1Id())) {
            sb.append("&category3Id=").append(searchParamDTO.getCategory3Id());
        }
        //  拼接keyword参数
        if (!StringUtils.isEmpty(searchParamDTO.getKeyword())) {
            sb.append("&keyword=").append(searchParamDTO.getKeyword());
        }
        // 拼接品牌查询条件
        //trademark=1:小米
        if (!StringUtils.isEmpty(searchParamDTO.getTrademark())) {
            sb.append("&trademark=").append(searchParamDTO.getTrademark());
        }
        // 拼接平台属性查询条件
        String[] props = searchParamDTO.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                //&props=24:128G:机身内存
                sb.append("&props=").append(prop);
            }
        }
        return sb.toString();
    }
}
















